classdef CartoonEstimator < handle

	properties
	imagesT;
	imagesE;
	motions;
	featCalculator;
	featuresT;
	coordinatesT;
	matcher;  % for kNN
	end

	methods
	function obj = CartoonEstimator(varargin)
		root = CONFIG.SILHOUETTE_PATH;
		obj.imagesT = ImageFileCollection('root', root);

		obj.motions = MotionCollection(CONFIG.MOTION_PATH, 24, ...
			'partitionStr', 'exp');
		obj.imagesT.associatedMotions = obj.motions;

	end

	function [] = train(obj)
		obj.featCalculator = FeatureCalculator(obj.imagesT);
		
		% indicator = featureSelection(obj.featCalculator, 300);
		mask = FeatureCalculator.makeFeatureMask();
		indicator = mask.HU | mask.SC;
		fprintf('nDimSelected: %d\n', sum(indicator));
		obj.featCalculator.setFeatureSelector(indicator);

		obj.featuresT = obj.featCalculator.calculate('useSelector', true);

		% normalizes feat, whose values lie within different dynamic ranges
		[obj.featuresT, obj.featCalculator.normalize] = ...
			FeatureCalculator.normalizerLerp(obj.featuresT);

		obj.coordinatesT = obj.featCalculator.calculatePoses();
		obj.matcher = ExhaustiveSearcher(obj.featuresT);
	end

	function [] = recover(obj, cartoonPath)
		% obj.imagesE = ImageFileCollection('root', cartoonPath);
		obj.imagesE = ImageFileCollection('root', cartoonPath, ...
			'preprocessor', @extractSilhouette);
		obj.featCalculator.imageCollection = obj.imagesE;

		fid = fopen('matchResult.html', 'w');
		CartoonEstimator.writeHtmlHeader(fid);

		for i = 1:obj.imagesE.nImages
			feat = obj.featCalculator.calculate('useSelector', true, ...
				'indicesToCalc', i, 'normalization', true);
			nearestIdx = knnsearch(obj.matcher, feat, 'K', 1);

			imgSrc = fullfile(obj.imagesE.root, obj.imagesE.database{i});
			imgDst = fullfile(obj.imagesT.root, obj.imagesT.database{nearestIdx});
			CartoonEstimator.writePairToHtml(fid, i, imgSrc, imgDst);

			% subplot(1, 2, 1);
			% imshow(obj.imagesE.at(i));
			% title({'Query image:', obj.imagesE.database{i}}, ...
			% 	'Interpreter', 'none');

			% subplot(1, 2, 2);
			% imshow(obj.imagesT.at(nearestIdx(1)));
			% title({'Matched:', obj.imagesT.database{nearestIdx(1)}}, ...
			% 	'Interpreter', 'none');
			% pause;
		end

		CartoonEstimator.writeHtmlEnd(fid);
		fclose(fid);
	end

	function [matchedCoords] = recoverDP(obj, cartoonPath, showResult)
		if ~exist('showResult', 'var')
			showResult = 'gui';
		end

		% obj.imagesE = ImageFileCollection('root', cartoonPath);
		obj.imagesE = ImageFileCollection('root', cartoonPath, ...
			'preprocessor', @extractSilhouette);
		obj.featCalculator.imageCollection = obj.imagesE;
		
		%--------8<----------------
		function cost = transitionCost(p, q)
			sigma2 = 25;  % parameter sigma = 5
			cost = poseDistance(obj.coordinatesT(p, :), ...
				obj.coordinatesT(q, :));
			cost = exp(-cost / sigma2);
		end
		%---------------->8--------

		averageError = 0;

		nImages = obj.imagesE.nImages;
		optimizer = SequentialOptimizer(nImages, @transitionCost);

		for i = 1:obj.imagesE.nImages
			feat = obj.featCalculator.calculate('useSelector', true, ...
				'indicesToCalc', i, 'normalization', true);
			[nearestIdx, distances] = knnsearch(obj.matcher, feat, 'K', 5);
			distances = distances / sum(distances);  % normalization
			optimizer.newFrame(distances, nearestIdx);
		end

		choices = optimizer.makeChoices();

		% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %
		% (*) writes result to HTML files
		if strcmpi(showResult, 'html')
			fid = fopen('matchResultDP.html', 'w');
			CartoonEstimator.writeHtmlHeader(fid);

			for i = 1:obj.imagesE.nImages
				imgSrc = fullfile(obj.imagesE.root, obj.imagesE.database{i});
				imgDst = fullfile(obj.imagesT.root, obj.imagesT.database{choices(i)});
				CartoonEstimator.writePairToHtml(fid, i, imgSrc, imgDst);
			end

			CartoonEstimator.writeHtmlEnd(fid);
			fclose(fid);

		% (*) shows by interactive uicontrols
		elseif strcmpi(showResult, 'gui')
			matchedCoords = obj.coordinatesT(choices, :);
			SkeletonDrawer.drawManyInterp(matchedCoords, @(i) ...
				imshow(imread(fullfile(obj.imagesE.root, obj.imagesE.database{i}))));

		% (*) shows on figure, one by one
		elseif strcmpi(showResult, 'figure')
			for i = 1:obj.imagesE.nImages
				matchedCoords(i, :) = obj.coordinatesT(choices(i), :);
				if ~showResult
					continue;
				end

				subplot(1, 4, 1);
				imshow(obj.imagesE.at(i));
				fprintf('Query image: %s\n', obj.imagesE.database{i});

				subplot(1, 4, 2);
				imshow(obj.imagesT.at(choices(i)));
				fprintf('Matched: %s\n', obj.imagesT.database{choices(i)});

				subplot(1, 4, 3:4);
				SkeletonDrawer.draw(obj.coordinatesT(choices(i), :));
				pause;
			end
		else
			warning('Invalid `showResult` parameter');
		end
	end
	end

	methods (Static)
	function [] = writeHtmlHeader(fid)
		fprintf(fid, ['<!DOCTYPE html><head><title>Matching result ', ...
			'(Left: query image, Right: matched one)</title>\n', ...
			'<style type="text/css">', ...
			'img { height:200px; display:inline; margin-left:50px; vertical-align:middle; }\n', ...
			'h2 { display:inline-block; }\n', ...
			'hr { width:60%%; }\n', ...
			'</style></head><body>\n']);
	end

	function [] = writePairToHtml(fid, index, testPath, matchedPath)
		fprintf(fid, '<div align="center"><h2>#%d </h2>\n', index);
		fprintf(fid, '<a href="file:///%s"><img src="file:///%s"/></a>\n', ...
			testPath, testPath);
		fprintf(fid, '<a href="file:///%s"><img src="file:///%s"/></a>\n', ...
			matchedPath, matchedPath);
		fprintf(fid, '</div><hr>\n');
	end

	function [] = writeHtmlEnd(fid)
		fprintf(fid, '</body>\n</html>\n');
	end
	end
end